home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 037a / ccunzip.zip / UNZ4.PAS < prev    next >
Pascal/Delphi Source File  |  1990-04-05  |  34KB  |  1,263 lines

  1.  
  2. (*
  3.  * Copyright 1987, 1989 Samuel H. Smith;  All rights reserved
  4.  *
  5.  * This is a component of the ProDoor System.
  6.  * Do not distribute modified versions without my permission.
  7.  * Do not remove or alter this notice or any other copyright notice.
  8.  * If you use this in your own program you must distribute source code.
  9.  * Do not use any of this in a commercial product.
  10.  *
  11.  *)
  12.  
  13. (*
  14.  * UnZip - A simple zipfile extract utility
  15.  *
  16.  *)
  17.  
  18. {$I+}                             {I/O checking}
  19. {$N-}                             {Numeric coprocessor}
  20. {$V-}                             {Relaxes string typing}
  21. {$B-}                             {Boolean complete evaluation}
  22. {$S-}                             {Stack checking}
  23. {$R-}                             {Range checking}
  24. {$D+}                             {Global debug information}
  25. {$L+}                             {Local debug information}
  26.  
  27. {$M 5000,0,0}                     {minstack,minheap,maxheap}
  28.  
  29. program UnZip;
  30.  
  31. uses
  32.   Dos, Mdosio, crc;
  33.  
  34. const
  35.   version        = 'UnZ:  Zipfile Extract v2.0 (PAS) of 09-09-89;  (C) 1989 S.H.Smith';
  36.  
  37.  
  38.  
  39. (*
  40.  * Data declarations for the archive text-view functions.
  41.  *
  42.  *)
  43.  
  44.   (* ----------------------------------------------------------- *)
  45. (*
  46.  * ZIPfile layout declarations
  47.  *
  48.  *)
  49.  
  50. type
  51.   signature_type = LongInt;
  52.  
  53. const
  54.   local_file_header_signature = $04034b50;
  55.  
  56. type
  57.   local_file_header = record
  58.                         version_needed_to_extract : Word;
  59.                         general_purpose_bit_flag : Word;
  60.                         compression_method : Word;
  61.                         last_mod_file_time : Word;
  62.                         last_mod_file_date : Word;
  63.                         crc32          : LongInt;
  64.                         compressed_size : LongInt;
  65.                         uncompressed_size : LongInt;
  66.                         filename_length : Word;
  67.                         extra_field_length : Word;
  68.                       end;
  69.  
  70. const
  71.   central_file_header_signature = $02014b50;
  72.  
  73. type
  74.   central_directory_file_header = record
  75.                                     version_made_by : Word;
  76.                                     version_needed_to_extract : Word;
  77.                                     general_purpose_bit_flag : Word;
  78.                                     compression_method : Word;
  79.                                     last_mod_file_time : Word;
  80.                                     last_mod_file_date : Word;
  81.                                     crc32          : LongInt;
  82.                                     compressed_size : LongInt;
  83.                                     uncompressed_size : LongInt;
  84.                                     filename_length : Word;
  85.                                     extra_field_length : Word;
  86.                                     file_comment_length : Word;
  87.                                     disk_number_start : Word;
  88.                                     internal_file_attributes : Word;
  89.                                     external_file_attributes : LongInt;
  90.                                     relative_offset_local_header : LongInt;
  91.                                   end;
  92.  
  93. const
  94.   end_central_dir_signature = $06054b50;
  95.  
  96. type
  97.   end_central_dir_record = record
  98.                              number_this_disk : Word;
  99.                              number_disk_with_start_central_directory : Word;
  100.                              total_entries_central_dir_on_this_disk : Word;
  101.                              total_entries_central_dir : Word;
  102.                              size_central_directory : LongInt;
  103.                              offset_start_central_directory : LongInt;
  104.                              zipfile_comment_length : Word;
  105.                            end;
  106.  
  107.  
  108.  
  109.   (* ----------------------------------------------------------- *)
  110. (*
  111.  * input file variables
  112.  *
  113.  *)
  114.  
  115. const
  116.   uinbufsize     = 512;           {input buffer size}
  117. var
  118.   zipeof         : Boolean;
  119.   Crc32Val       : LongInt;
  120.   InCrc          : LongInt;
  121.   csize          : LongInt;
  122.   cusize         : LongInt;
  123.   cmethod        : Integer;
  124.   cflags         : Word;
  125.  
  126.   ctime          : Word;
  127.   cdate          : Word;
  128.   inbuf          : array[1..uinbufsize] of Byte;
  129.   inpos          : Integer;
  130.   incnt          : Integer;
  131.   pc             : Byte;
  132.   pcbits         : Byte;
  133.   pcbitv         : Byte;
  134.   zipfd          : dos_handle;
  135.   zipfn          : dos_filename;
  136.  
  137.  
  138.  
  139.   (* ----------------------------------------------------------- *)
  140. (*
  141.  * output stream variables
  142.  *
  143.  *)
  144.  
  145. var
  146.   outbuf         : array[0..8192] of Byte; {8192 or more for rle look-back}
  147.   outpos         : LongInt;       {absolute position in outfile}
  148.   outcnt         : Integer;
  149.   outfd          : dos_handle;
  150.   filename       : String;
  151.   extra          : String;
  152.  
  153.  
  154.  
  155.   (* ----------------------------------------------------------- *)
  156.  
  157. type
  158.   Sarray         = array[0..255] of String[64];
  159.  
  160. var
  161.   factor         : Integer;
  162.   followers      : Sarray;
  163.   ExState        : Integer;
  164.   C              : Integer;
  165.   V              : Integer;
  166.   Len            : Integer;
  167.  
  168. const
  169.   hsize          = 8192;
  170.  
  171. type
  172.   hsize_array_integer = array[0..hsize] of Integer;
  173.   hsize_array_byte = array[0..hsize] of Byte;
  174.  
  175. var
  176.   prefix_of      : hsize_array_integer;
  177.   suffix_of      : hsize_array_byte;
  178.   stack          : hsize_array_byte;
  179.   stackp         : Integer;
  180.  
  181. (*
  182.  * Zipfile input/output handlers
  183.  *
  184.  *)
  185.  
  186.  
  187.   (* ------------------------------------------------------------- *)
  188.   procedure skip_csize;
  189.   begin
  190.     dos_lseek(zipfd, csize, seek_cur);
  191.     zipeof := True;
  192.     csize := 0;
  193.     incnt := 0;
  194.   end;
  195.  
  196.  
  197.   (* ------------------------------------------------------------- *)
  198.   procedure ReadByte(var x : Byte);
  199.   begin
  200.     if inpos > incnt then
  201.       begin
  202.         if csize = 0 then
  203.           begin
  204.             zipeof := True;
  205.             Exit;
  206.           end;
  207.  
  208.         inpos := SizeOf(inbuf);
  209.         if inpos > csize then
  210.           inpos := csize;
  211.         incnt := dos_read(zipfd, inbuf, inpos);
  212.  
  213.         inpos := 1;
  214.         Dec(csize, incnt);
  215.       end;
  216.  
  217.     x := inbuf[inpos];
  218.     Inc(inpos);
  219.   end;
  220.  
  221.  
  222. (*
  223.  * Copyright 1987, 1989 Samuel H. Smith;  All rights reserved
  224.  *
  225.  * This is a component of the ProDoor System.
  226.  * Do not distribute modified versions without my permission.
  227.  * Do not remove or alter this notice or any other copyright notice.
  228.  * If you use this in your own program you must distribute source code.
  229.  * Do not use any of this in a commercial product.
  230.  *
  231.  *)
  232.  
  233. (******************************************************
  234.  *
  235.  * Procedure:  itohs
  236.  *
  237.  * Purpose:    converts an integer into a string of hex digits
  238.  *
  239.  * Example:    s := itohs(i);
  240.  *
  241.  *)
  242.  
  243.   function itohs(i : LongInt) : String; {integer to hex conversion}
  244.   var
  245.     h              : String;
  246.  
  247.     procedure digit(ix : Integer; ii : LongInt);
  248.     const
  249.      hexdigit:array[0..15] of char = ('0','1','2','3','4','5','6','7',
  250.                                       '8','9','A','B','C','D','E','F');
  251.     begin
  252.       ii := ii and 15;
  253.       h[ix] := hexdigit[ii];
  254.     end;
  255.  
  256.   begin
  257.     h[0] := Chr(8);
  258.     digit(1, i shr 28);
  259.     digit(2, i shr 24);
  260.     digit(3, i shr 20);
  261.     digit(4, i shr 16);
  262.     digit(5, i shr 12);
  263.     digit(6, i shr 8);
  264.     digit(7, i shr 4);
  265.     digit(8, i);
  266.     itohs := h;
  267.   end;
  268.  
  269.  
  270.   (* ------------------------------------------------------------- *)
  271.   procedure ReadBits(bits : Integer; var result : Integer);
  272.     {read the specified number of bits}
  273.   var
  274.     x, t, s, mask  : Integer;
  275.   begin
  276.     if (bits < pcbits)
  277.     then begin
  278.       mask := (1 shl bits)-1;
  279.       x := pc and mask;
  280.       pc := pc shr bits;
  281.       Dec(pcbits, bits);
  282.     end
  283.     else if (bits = pcbits)
  284.     then begin
  285.       x := pc;
  286.       pcbits := 0;
  287.       pc := 0;
  288.     end
  289.     else begin
  290.       x := pc;
  291.       Dec(bits, pcbits);
  292.       s := pcbits;
  293.       while (bits > 0) do
  294.         begin
  295.           ReadByte(pc);
  296.           if bits > 8 then t := 8 else t := bits;
  297.           mask := (1 shl t)-1;
  298.           x := x or ((pc and mask) shl s);
  299.           pcbits := 8-t;
  300.           Inc(s, 8);
  301.           Dec(bits, t);
  302.           pc := pc shr t;
  303.         end;
  304.     end;
  305.     result := x;
  306.   end;
  307.  
  308.  
  309.   (* ---------------------------------------------------------- *)
  310.   procedure get_string(ln : Word; var s : String);
  311.   var
  312.     n              : Word;
  313.   begin
  314.     if ln > 255 then
  315.       ln := 255;
  316.     n := dos_read(zipfd, s[1], ln);
  317.     s[0] := Chr(ln);
  318.   end;
  319.  
  320.  
  321.   (* ------------------------------------------------------------- *)
  322.   procedure OutByte(C : Integer);
  323.     (* output each character from archive to screen *)
  324.   begin
  325.     outbuf[outcnt {outpos mod sizeof(outbuf)} ] := C;
  326.     Inc(outpos);
  327.     Inc(outcnt);
  328.  
  329.     if outcnt = SizeOf(outbuf) then
  330.       begin
  331.         Crc32Val := UpdateCRC32(Crc32Val,outbuf,outcnt);
  332.         dos_write(outfd, outbuf, outcnt);
  333.         outcnt := 0;
  334.         Write('.');
  335.       end;
  336.   end;
  337.  
  338.  
  339. (*
  340.  * expand 'reduced' members of a zipfile
  341.  *
  342.  *)
  343.  
  344. (*
  345.  * The Reducing algorithm is actually a combination of two
  346.  * distinct algorithms.  The first algorithm compresses repeated
  347.  * byte sequences, and the second algorithm takes the compressed
  348.  * stream from the first algorithm and applies a probabilistic
  349.  * compression method.
  350.  *
  351.  *)
  352.  
  353.   function reduce_L(x : Byte) : Byte;
  354.   begin
  355.     case factor of
  356.       1 : reduce_L := x and $7f;
  357.       2 : reduce_L := x and $3f;
  358.       3 : reduce_L := x and $1f;
  359.       4 : reduce_L := x and $0f;
  360.     end;
  361.   end;
  362.  
  363.   function reduce_F(x : Byte) : Byte;
  364.   begin
  365.     case factor of
  366.       1 : if x = 127 then reduce_F := 2 else reduce_F := 3;
  367.       2 : if x = 63 then reduce_F := 2 else reduce_F := 3;
  368.       3 : if x = 31 then reduce_F := 2 else reduce_F := 3;
  369.       4 : if x = 15 then reduce_F := 2 else reduce_F := 3;
  370.     end;
  371.   end;
  372.  
  373.   function reduce_D(x, y : Byte) : Word;
  374.   begin
  375.     case factor of
  376.       1 : reduce_D := (((x shr 7) and $01) shl 8)+y+1;
  377.       2 : reduce_D := (((x shr 6) and $03) shl 8)+y+1;
  378.       3 : reduce_D := (((x shr 5) and $07) shl 8)+y+1;
  379.       4 : reduce_D := (((x shr 4) and $0f) shl 8)+y+1;
  380.     end;
  381.   end;
  382.  
  383.   function reduce_B(x : Byte) : Word;
  384.     {number of bits needed to encode the specified number}
  385.   begin
  386.     case x-1 of
  387.       0..1 : reduce_B := 1;
  388.       2..3 : reduce_B := 2;
  389.       4..7 : reduce_B := 3;
  390.       8..15 : reduce_B := 4;
  391.       16..31 : reduce_B := 5;
  392.       32..63 : reduce_B := 6;
  393.       64..127 : reduce_B := 7;
  394.     else reduce_B := 8;
  395.     end;
  396.   end;
  397.  
  398.   procedure Expand(C : Byte);
  399.   const
  400.     DLE            = 144;
  401.   var
  402.     op             : LongInt;
  403.     op_x           : LongInt;
  404.     i              : Integer;
  405.     temp           : Integer;
  406.  
  407.   begin
  408.  
  409.     case ExState of
  410.       0 : if C <> DLE then
  411.             OutByte(C)
  412.           else
  413.             ExState := 1;
  414.  
  415.       1 : if C <> 0 then
  416.             begin
  417.               V := C;
  418.               Len := reduce_L(V);
  419.               ExState := reduce_F(Len);
  420.             end
  421.           else
  422.             begin
  423.               OutByte(DLE);
  424.               ExState := 0;
  425.             end;
  426.  
  427.       2 : begin
  428.             Len := Len+C;
  429.             ExState := 3;
  430.           end;
  431.  
  432.       3 : begin
  433.             op := outpos-reduce_D(V, C);
  434.             if op >= SizeOf(outbuf)
  435.             then op_x := op mod SizeOf(outbuf)
  436.             else op_x := op;
  437.             for i := 0 to Len+2 do
  438.               begin
  439.                 if op < 0 then
  440.                   OutByte(0)
  441.                 else begin
  442.                   OutByte(outbuf[op_x]);
  443.                   end;
  444.                 Inc(op);
  445.                 Inc(op_x);
  446.                 if op_x >= SizeOf(outbuf) then op_x := 0;
  447.               end;
  448.  
  449.             ExState := 0;
  450.           end;
  451.     end;
  452.   end;
  453.  
  454.  
  455.   procedure LoadFollowers;
  456.   var
  457.     x              : Integer;
  458.     i              : Integer;
  459.     b              : Integer;
  460.   begin
  461.     for x := 255 downto 0 do
  462.       begin
  463.         ReadBits(6, b);
  464.         followers[x][0] := Chr(b);
  465.  
  466.         for i := 1 to Length(followers[x]) do
  467.           begin
  468.             ReadBits(8, b);
  469.             followers[x][i] := Chr(b);
  470.           end;
  471.       end;
  472.   end;
  473.  
  474.  
  475.   (* ----------------------------------------------------------- *)
  476.   procedure unReduce;
  477.     {expand probablisticly reduced data}
  478.  
  479.   var
  480.     lchar          : Integer;
  481.     lout           : Integer;
  482.     i              : Integer;
  483.  
  484.   begin
  485.     factor := cmethod-1;
  486.     if (factor < 1) or (factor > 4) then
  487.       begin
  488.         skip_csize;
  489.         Exit;
  490.       end;
  491.  
  492.     ExState := 0;
  493.     LoadFollowers;
  494.     lchar := 0;
  495.  
  496.     while (not zipeof) and (outpos < cusize) do
  497.       begin
  498.  
  499.         if followers[lchar] = '' then
  500.           ReadBits(8, lout)
  501.         else
  502.  
  503.           begin
  504.             ReadBits(1, lout);
  505.             if lout <> 0 then
  506.               ReadBits(8, lout)
  507.             else
  508.               begin
  509.                 ReadBits(reduce_B(Length(followers[lchar])), i);
  510.                 lout := Ord(followers[lchar][i+1]);
  511.               end;
  512.           end;
  513.  
  514.         if zipeof then
  515.           Exit;
  516.  
  517.         Expand(lout);
  518.         lchar := lout;
  519.       end;
  520.  
  521.   end;
  522.  
  523.  
  524.  
  525. (*
  526.  * expand 'shrunk' members of a zipfile
  527.  *
  528.  *)
  529.  
  530. (*
  531.  * UnShrinking
  532.  * -----------
  533.  *
  534.  * Shrinking is a Dynamic Ziv-Lempel-Welch compression algorithm
  535.  * with partial clearing.  The initial code size is 9 bits, and
  536.  * the maximum code size is 13 bits.  Shrinking differs from
  537.  * conventional Dynamic Ziv-lempel-Welch implementations in several
  538.  * respects:
  539.  *
  540.  * 1)  The code size is controlled by the compressor, and is not
  541.  *     automatically increased when codes larger than the current
  542.  *     code size are created (but not necessarily used).  When
  543.  *     the decompressor encounters the code sequence 256
  544.  *     (decimal) followed by 1, it should increase the code size
  545.  *     read from the input stream to the next bit size.  No
  546.  *     blocking of the codes is performed, so the next code at
  547.  *     the increased size should be read from the input stream
  548.  *     immediately after where the previous code at the smaller
  549.  *     bit size was read.  Again, the decompressor should not
  550.  *     increase the code size used until the sequence 256,1 is
  551.  *     encountered.
  552.  *
  553.  * 2)  When the table becomes full, total clearing is not
  554.  *     performed.  Rather, when the compresser emits the code
  555.  *     sequence 256,2 (decimal), the decompressor should clear
  556.  *     all leaf nodes from the Ziv-Lempel tree, and continue to
  557.  *     use the current code size.  The nodes that are cleared
  558.  *     from the Ziv-Lempel tree are then re-used, with the lowest
  559.  *     code value re-used first, and the highest code value
  560.  *     re-used last.  The compressor can emit the sequence 256,2
  561.  *     at any time.
  562.  *
  563.  *)
  564.  
  565.   procedure unShrink;
  566.  
  567.   const
  568.     max_bits       = 13;
  569.     init_bits      = 9;
  570.     first_ent      = 257;
  571.     clear          = 256;
  572.  
  573.   var
  574.     cbits          : Integer;
  575.     maxcode        : Integer;
  576.     free_ent       : Integer;
  577.     maxcodemax     : Integer;
  578.     offset         : Integer;
  579.     sizex          : Integer;
  580.     finchar        : Integer;
  581.     code           : Integer;
  582.     oldcode        : Integer;
  583.     incode         : Integer;
  584.  
  585.  
  586.     (* ------------------------------------------------------------- *)
  587.     procedure partial_clear;
  588.     var
  589.       pr             : Integer;
  590.       cd             : Integer;
  591.  
  592.     begin
  593.       {mark all nodes as potentially unused}
  594.       for cd := first_ent to free_ent-1 do
  595.         Word(prefix_of[cd]) := prefix_of[cd] or $8000;
  596.  
  597.  
  598.       {unmark those that are used by other nodes}
  599.       for cd := first_ent to free_ent-1 do
  600.         begin
  601.           pr := prefix_of[cd] and $7fff; {reference to another node?}
  602.           if pr >= first_ent then {flag node as referenced}
  603.             prefix_of[pr] := prefix_of[pr] and $7fff;
  604.         end;
  605.  
  606.  
  607.       {clear the ones that are still marked}
  608.       for cd := first_ent to free_ent-1 do
  609.         if (prefix_of[cd] and $8000) <> 0 then
  610.           prefix_of[cd] := -1;
  611.  
  612.  
  613.       {find first cleared node as next free_ent}
  614.       free_ent := first_ent;
  615.       while (free_ent < maxcodemax) and (prefix_of[free_ent] <> -1) do
  616.         Inc(free_ent);
  617.     end;
  618.  
  619.  
  620.     (* ------------------------------------------------------------- *)
  621.   begin
  622.     (* decompress the file *)
  623.     maxcodemax := 1 shl max_bits;
  624.     cbits := init_bits;
  625.     maxcode := (1 shl cbits)-1;
  626.     free_ent := first_ent;
  627.     offset := 0;
  628.     sizex := 0;
  629.  
  630.     FillChar(prefix_of, SizeOf(prefix_of), $FF);
  631.     for code := 255 downto 0 do
  632.       begin
  633.         prefix_of[code] := 0;
  634.         suffix_of[code] := code;
  635.       end;
  636.  
  637.     ReadBits(cbits, oldcode);
  638.     if zipeof then
  639.       Exit;
  640.     finchar := oldcode;
  641.  
  642.     OutByte(finchar);
  643.  
  644.     stackp := 0;
  645.  
  646.     while (not zipeof) do
  647.       begin
  648.         ReadBits(cbits, code);
  649.         if zipeof then
  650.           Exit;
  651.  
  652.         while (code = clear) do
  653.           begin
  654.             ReadBits(cbits, code);
  655.  
  656.             case code of
  657.               1 : begin
  658.                     Inc(cbits);
  659.                     if cbits = max_bits then
  660.                       maxcode := maxcodemax
  661.                     else
  662.                       maxcode := (1 shl cbits)-1;
  663.                   end;
  664.  
  665.               2 : partial_clear;
  666.             end;
  667.  
  668.             ReadBits(cbits, code);
  669.             if zipeof then
  670.               Exit;
  671.           end;
  672.  
  673.  
  674.         {special case for KwKwK string}
  675.         incode := code;
  676.         if prefix_of[code] = -1 then
  677.           begin
  678.             stack[stackp] := finchar;
  679.             Inc(stackp);
  680.             code := oldcode;
  681.           end;
  682.  
  683.  
  684.         {generate output characters in reverse order}
  685.         while (code >= first_ent) do
  686.           begin
  687.             stack[stackp] := suffix_of[code];
  688.             Inc(stackp);
  689.             code := prefix_of[code];
  690.           end;
  691.  
  692.         finchar := suffix_of[code];
  693.         stack[stackp] := finchar;
  694.         Inc(stackp);
  695.  
  696.  
  697.         {and put them out in forward order}
  698.         while (stackp > 0) do
  699.           begin
  700.             Dec(stackp);
  701.             OutByte(stack[stackp]);
  702.           end;
  703.  
  704.  
  705.         {generate new entry}
  706.         code := free_ent;
  707.         if code < maxcodemax then
  708.           begin
  709.             prefix_of[code] := oldcode;
  710.             suffix_of[code] := finchar;
  711.             while (free_ent < maxcodemax) and (prefix_of[free_ent] <> -1) do
  712.               Inc(free_ent);
  713.           end;
  714.  
  715.  
  716.         {remember previous code}
  717.         oldcode := incode;
  718.       end;
  719.  
  720.   end;
  721.  
  722.  
  723.  
  724.   (* ------------------------------------------------------------- *)
  725. (*
  726.  * Imploding
  727.  * ---------
  728.  *
  729.  * The Imploding algorithm is actually a combination of two distinct
  730.  * algorithms.  The first algorithm compresses repeated byte sequences
  731.  * using a sliding dictionary.  The second algorithm is used to compress
  732.  * the encoding of the sliding dictionary ouput, using multiple
  733.  * Shannon-Fano trees.
  734.  *
  735.  *)
  736.  
  737. const
  738.   maxSF          = 256;
  739.  
  740. type
  741.   sf_entry       = record
  742.                      code           : Word;
  743.                      Value          : Byte;
  744.                      BitLength      : Byte;
  745.                    end;
  746.  
  747.   sf_tree        = record         {a shannon-fano tree}
  748.                      entry          : array[0..maxSF] of sf_entry;
  749.                      entries        : Integer;
  750.                      MaxLength      : Integer;
  751.                    end;
  752.  
  753.   sf_treep       = ^sf_tree;
  754.  
  755. var
  756.   lit_tree       : sf_tree;
  757.   length_tree    : sf_tree;
  758.   distance_tree  : sf_tree;
  759.   lit_tree_present : Boolean;
  760.   eightK_dictionary : Boolean;
  761.   minimum_match_length : Integer;
  762.   dict_bits      : Integer;
  763.  
  764.  
  765.   {$I UNZSORT.INC}
  766.  
  767.   (* ----------------------------------------------------------- *)
  768.   procedure ReadLengths(var tree : sf_tree);
  769.   var
  770.     treeBytes      : Integer;
  771.     i, j, k        : Integer;
  772.     num, Len       : Integer;
  773.  
  774.   begin
  775.     {get number of bytes in compressed tree}
  776.     ReadBits(8, treeBytes);
  777.     Inc(treeBytes);
  778.     i := 0;
  779.  
  780.     begin
  781.       tree.MaxLength := 0;
  782.  
  783.       {High 4 bits: Number of values at this bit length + 1. (1 - 16)
  784.        Low  4 bits: Bit Length needed to represent value + 1. (1 - 16)}
  785.       for j := 1 to treeBytes do
  786.         begin
  787.           ReadBits(4, Len); Inc(Len);
  788.           ReadBits(4, num); Inc(num);
  789.  
  790.           for k := i to i+num-1 do
  791.             begin
  792.               if Len > tree.MaxLength then
  793.                 tree.MaxLength := Len;
  794.               tree.entry[k].BitLength := Len;
  795.               tree.entry[k].Value := k;
  796.             end;
  797.           Inc(i, num);
  798.  
  799.           Dec(treeBytes);
  800.         end;
  801.     end;
  802.   end;
  803.  
  804.  
  805.   (* ----------------------------------------------------------- *)
  806.   procedure GenerateTrees(var tree : sf_tree);
  807.     {Generate the Shannon-Fano trees}
  808.   var
  809.     code           : Word;
  810.     CodeIncrement  : Integer;
  811.     LastBitLength  : Integer;
  812.     i              : Integer;
  813.  
  814.   begin
  815.     code := 0;
  816.     CodeIncrement := 0;
  817.     LastBitLength := 0;
  818.  
  819.     i := tree.entries-1;          {either 255 or 63}
  820.     while i >= 0 do
  821.       begin
  822.         Inc(code, CodeIncrement);
  823.         if tree.entry[i].BitLength <> LastBitLength then
  824.           begin
  825.             LastBitLength := tree.entry[i].BitLength;
  826.             CodeIncrement := 1 shl (16-LastBitLength);
  827.           end;
  828.  
  829.         tree.entry[i].code := code;
  830.         Dec(i);
  831.       end;
  832.   end;
  833.  
  834.  
  835.   (* ----------------------------------------------------------- *)
  836.   procedure ReverseBits(var tree : sf_tree);
  837.    {Reverse the order of all the bits in the above ShannonCode[]
  838.     vector, so that the most significant bit becomes the least
  839.     significant bit. For example, the value 0x1234 (hex) would become
  840.     0x2C48 (hex).}
  841.   var
  842.     i              : Integer;
  843.     V              : Word;
  844.     o              : Word;
  845.  
  846.   begin
  847.     for i := 0 to tree.entries-1 do
  848.       begin
  849.         {get original code}
  850.         o := tree.entry[i].code;
  851.         V := 0;
  852.         {reverse each bit}
  853.         if (o and $0001) <> 0 then V := $8000;
  854.         if (o and $0002) <> 0 then V := V or $4000;
  855.         if (o and $0004) <> 0 then V := V or $2000;
  856.         if (o and $0008) <> 0 then V := V or $1000;
  857.         if (o and $0010) <> 0 then V := V or $0800;
  858.         if (o and $0020) <> 0 then V := V or $0400;
  859.         if (o and $0040) <> 0 then V := V or $0200;
  860.         if (o and $0080) <> 0 then V := V or $0100;
  861.         if (o and $0100) <> 0 then V := V or $0080;
  862.         if (o and $0200) <> 0 then V := V or $0040;
  863.         if (o and $0400) <> 0 then V := V or $0020;
  864.         if (o and $0800) <> 0 then V := V or $0010;
  865.         if (o and $1000) <> 0 then V := V or $0008;
  866.         if (o and $2000) <> 0 then V := V or $0004;
  867.         if (o and $4000) <> 0 then V := V or $0002;
  868.         if (o and $8000) <> 0 then V := V or $0001;
  869.  
  870.         {store reversed bits}
  871.         tree.entry[i].code := V;
  872.       end;
  873.   end;
  874.  
  875.  
  876.   (* ----------------------------------------------------------- *)
  877.   procedure LoadTree(var tree       : sf_tree;
  878.                      treesize       : Integer);
  879.     {allocate and load a shannon-fano tree from the compressed file}
  880.   begin
  881.     tree.entries := treesize;
  882.     ReadLengths(tree);
  883.     SortLengths(tree);
  884.     GenerateTrees(tree);
  885.     ReverseBits(tree);
  886.   end;
  887.  
  888.  
  889.   (* ----------------------------------------------------------- *)
  890.   procedure LoadTrees;
  891.   begin
  892.     eightK_dictionary := (cflags and $02) <> 0; {bit 1}
  893.     lit_tree_present := (cflags and $04) <> 0; {bit 2}
  894.  
  895.     if eightK_dictionary then
  896.       dict_bits := 7
  897.     else
  898.       dict_bits := 6;
  899.  
  900.     if lit_tree_present then
  901.       begin
  902.         minimum_match_length := 3;
  903.         LoadTree(lit_tree, 256);
  904.       end
  905.     else
  906.       minimum_match_length := 2;
  907.  
  908.     LoadTree(length_tree, 64);
  909.     LoadTree(distance_tree, 64);
  910.   end;
  911.  
  912.  
  913.   (* ----------------------------------------------------------- *)
  914.   procedure ReadTree(var tree       : sf_tree;
  915.                      var dest       : Integer);
  916.     {read next byte using a shannon-fano tree}
  917.   var
  918.     bits           : Integer;
  919.     cv             : Word;
  920.     b              : Integer;
  921.     cur            : Integer;
  922.  
  923.   begin
  924.     bits := 0;
  925.     cv := 0;
  926.     cur := 0;
  927.     dest := -1;                   {in case of error}
  928.  
  929.     while True do
  930.       begin
  931.         ReadBits(1, b);
  932.         cv := cv or (b shl bits);
  933.         Inc(bits);
  934.  
  935.       (* this is a very poor way of decoding shannon-fano.  two quicker
  936.       methods come to mind:
  937.          a) arrange the tree as a huffman-style binary tree with
  938.             a "leaf" indicator at each node,
  939.       and
  940.          b) take advantage of the fact that s-f codes are at most 8
  941.             bits long and alias unused codes for all bits following
  942.             the "leaf" bit.
  943.       *)
  944.  
  945.         while tree.entry[cur].BitLength < bits do
  946.           begin
  947.             Inc(cur);
  948.             if cur >= tree.entries then
  949.               Exit;
  950.           end;
  951.  
  952.         while tree.entry[cur].BitLength = bits do
  953.           begin
  954.             if tree.entry[cur].code = cv then
  955.               begin
  956.                 dest := tree.entry[cur].Value;
  957.                 Exit;
  958.               end;
  959.  
  960.             Inc(cur);
  961.             if cur >= tree.entries then
  962.               Exit;
  963.           end;
  964.       end;
  965.   end;
  966.  
  967.  
  968.   (* ----------------------------------------------------------- *)
  969.   procedure unImplode;
  970.     {expand imploded data}
  971.  
  972.   var
  973.     lout           : Integer;
  974.     op             : LongInt;
  975.     op_x           : LongInt;
  976.     Length         : Integer;
  977.     Distance       : Integer;
  978.     i              : Integer;
  979.     temp           : Integer;
  980.  
  981.   begin
  982.     LoadTrees;
  983.  
  984.     while (not zipeof) and (outpos < cusize) do
  985.       begin
  986.         ReadBits(1, lout);
  987.  
  988.         if lout <> 0 then         {encoded data is literal data}
  989.           begin
  990.             if lit_tree_present then
  991.               ReadTree(lit_tree, lout) {use Literal Shannon-Fano tree}
  992.             else
  993.               ReadBits(8, lout);
  994.  
  995.             OutByte(lout);
  996.           end
  997.         else
  998.  
  999.           begin                   {encoded data is sliding dictionary match}
  1000.             ReadBits(dict_bits, lout);
  1001.             Distance := lout;
  1002.  
  1003.             ReadTree(distance_tree, lout);
  1004.             Distance := Distance or (lout shl dict_bits);
  1005.          {using the Distance Shannon-Fano tree, read and decode the
  1006.             upper 6 bits of the Distance value}
  1007.  
  1008.             ReadTree(length_tree, Length);
  1009.             {using the Length Shannon-Fano tree, read and decode the Length value}
  1010.  
  1011.             Inc(Length, minimum_match_length);
  1012.             if Length = (63+minimum_match_length) then
  1013.               begin
  1014.                 ReadBits(8, lout);
  1015.                 Inc(Length, lout);
  1016.               end;
  1017.  
  1018.          {move backwards Distance+1 bytes in the output stream, and copy
  1019.           Length characters from this position to the output stream.
  1020.           (if this position is before the start of the output stream,
  1021.           then assume that all the data before the start of the output
  1022.           stream is filled with zeros)}
  1023.  
  1024.             op := outpos-Distance-1;
  1025.             if op >= SizeOf(outbuf)
  1026.             then op_x := op mod SizeOf(outbuf)
  1027.             else op_x := op;
  1028.             for i := 1 to Length do
  1029.               begin
  1030.                 if op < 0 then
  1031.                   OutByte(0)
  1032.                 else
  1033.                   OutByte(outbuf[op_x]);
  1034.                 Inc(op);
  1035.                 Inc(op_x);
  1036.                 if op_x >= SizeOf(outbuf) then op_x := 0;
  1037.               end;
  1038.           end;
  1039.       end;
  1040.   end;
  1041.  
  1042.  
  1043.  
  1044. (*
  1045.  * This procedure displays the text contents of a specified archive
  1046.  * file.  The filename must be fully specified and verified.
  1047.  *
  1048.  *)
  1049.  
  1050.  
  1051.   (* ---------------------------------------------------------- *)
  1052.   procedure extract_member;
  1053.   var
  1054.     b              : Byte;
  1055.  
  1056.   begin
  1057.     pcbits := 0;
  1058.     pc := 0;
  1059.     incnt := 0;
  1060.     inpos := 1+SizeOf(inbuf);
  1061.     outpos := 0;
  1062.     outcnt := 0;
  1063.     zipeof := False;
  1064.     Crc32Val := -1;
  1065.  
  1066.     outfd := dos_create(filename);
  1067.     if outfd = dos_error then
  1068.       begin
  1069.         WriteLn('Can''t create output: ', filename);
  1070.         Halt;
  1071.       end;
  1072.  
  1073.     case cmethod of
  1074.       0 :                         {stored}
  1075.         begin
  1076.           Write(' Extract: ', filename, ' ...');
  1077.           ReadByte(b);
  1078.           while (not zipeof) do
  1079.             begin
  1080.               OutByte(b);
  1081.               ReadByte(b);
  1082.             end;
  1083.         end;
  1084.  
  1085.       1 : begin
  1086.             Write('UnShrink: ', filename, ' ...');
  1087.             unShrink;
  1088.           end;
  1089.  
  1090.       2..5 : begin
  1091.                Write('  Expand: ', filename, ' ...');
  1092.                unReduce;
  1093.              end;
  1094.  
  1095.       6 : begin
  1096.             Write(' Explode: ', filename, ' ...');
  1097.             unImplode;
  1098.           end;
  1099.  
  1100.     else Write('Unknown compression method.');
  1101.     end;
  1102.  
  1103.     if outcnt > 0
  1104.     then begin
  1105.          Crc32Val := UpdateCRC32(Crc32Val,outbuf,outcnt);
  1106.          dos_write(outfd, outbuf, outcnt);
  1107.          end;
  1108.  
  1109.     dos_file_times(outfd, time_set, ctime, cdate);
  1110.     dos_close(outfd);
  1111.     Crc32Val := not Crc32Val;
  1112.     if Crc32Val <> InCrc
  1113.     then begin
  1114.       WriteLn('WARNING - preceeding fails CRC check.');
  1115.       WriteLn('Stored CRC=', itohs(InCrc));
  1116.       WriteLn('Calculated CRC=', itohs(Crc32Val));
  1117.     end;
  1118.  
  1119.     WriteLn('  done.');
  1120.   end;
  1121.  
  1122.  
  1123.   (* ---------------------------------------------------------- *)
  1124.   procedure process_local_file_header;
  1125.   var
  1126.     n              : Word;
  1127.     rec            : local_file_header;
  1128.  
  1129.   begin
  1130.     n := dos_read(zipfd, rec, SizeOf(rec));
  1131.     get_string(rec.filename_length, filename);
  1132.     get_string(rec.extra_field_length, extra);
  1133.     csize := rec.compressed_size;
  1134.     cusize := rec.uncompressed_size;
  1135.     cmethod := rec.compression_method;
  1136.     cflags := rec.general_purpose_bit_flag;
  1137.     ctime := rec.last_mod_file_time;
  1138.     cdate := rec.last_mod_file_date;
  1139.     InCrc := rec.crc32;
  1140.     extract_member;
  1141.   end;
  1142.  
  1143.  
  1144.   (* ---------------------------------------------------------- *)
  1145.   procedure process_central_file_header;
  1146.   var
  1147.     n              : Word;
  1148.     rec            : central_directory_file_header;
  1149.     filename       : String;
  1150.     extra          : String;
  1151.     comment        : String;
  1152.  
  1153.   begin
  1154.     n := dos_read(zipfd, rec, SizeOf(rec));
  1155.     get_string(rec.filename_length, filename);
  1156.     get_string(rec.extra_field_length, extra);
  1157.     get_string(rec.file_comment_length, comment);
  1158.   end;
  1159.  
  1160.  
  1161.   (* ---------------------------------------------------------- *)
  1162.   procedure process_end_central_dir;
  1163.   var
  1164.     n              : Word;
  1165.     rec            : end_central_dir_record;
  1166.     comment        : String;
  1167.  
  1168.   begin
  1169.     n := dos_read(zipfd, rec, SizeOf(rec));
  1170.     get_string(rec.zipfile_comment_length, comment);
  1171.   end;
  1172.  
  1173.  
  1174.   (* ---------------------------------------------------------- *)
  1175.   procedure process_headers;
  1176.   var
  1177.     sig            : LongInt;
  1178.  
  1179.   begin
  1180.     dos_lseek(zipfd, 0, seek_start);
  1181.  
  1182.     while True do
  1183.       begin
  1184.         if dos_read(zipfd, sig, SizeOf(sig)) <> SizeOf(sig) then
  1185.           Exit
  1186.         else
  1187.  
  1188.           if sig = local_file_header_signature then
  1189.             process_local_file_header
  1190.           else
  1191.  
  1192.             if sig = central_file_header_signature then
  1193.               process_central_file_header
  1194.             else
  1195.  
  1196.               if sig = end_central_dir_signature then
  1197.                 begin
  1198.                   process_end_central_dir;
  1199.                   Exit;
  1200.                 end
  1201.  
  1202.               else
  1203.                 begin
  1204.                   WriteLn('Invalid Zipfile Header');
  1205.                   Exit;
  1206.                 end;
  1207.       end;
  1208.  
  1209.   end;
  1210.  
  1211.  
  1212.   (* ---------------------------------------------------------- *)
  1213.   procedure extract_zipfile;
  1214.   begin
  1215.     zipfd := dos_open(zipfn, open_read);
  1216.     if zipfd = dos_error then
  1217.       Exit;
  1218.  
  1219.     process_headers;
  1220.  
  1221.     dos_close(zipfd);
  1222.   end;
  1223.  
  1224.  
  1225. (*
  1226.  * main program
  1227.  *
  1228.  *)
  1229.  
  1230. begin
  1231.   if ParamCount <> 1 then
  1232.     begin
  1233.       WriteLn;
  1234.       WriteLn(version);
  1235.       WriteLn('Courtesy of:  S.H.Smith  and  The Tool Shop BBS,  (602) 279-2673.');
  1236.       WriteLn;
  1237.       WriteLn('You may copy and distribute this program freely, provided that:');
  1238.       WriteLn('    1)   No fee is charged for such copying and distribution, and');
  1239.       WriteLn('    2)   It is distributed ONLY in its original, unmodified state.');
  1240.       WriteLn('If you wish to distribute a modified version of this program, you MUST');
  1241.       WriteLn('include the source code.');
  1242.       WriteLn;
  1243.       WriteLn('If you modify this program, I would appreciate a copy of the  new source');
  1244.       WriteLn('code.   I am holding the copyright on the source code, so please don''t');
  1245.       WriteLn('delete my name from the program files or from the documentation.');
  1246.       WriteLn('IN NO EVENT WILL I BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING ANY LOST');
  1247.       WriteLn('PROFITS, LOST SAVINGS OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES');
  1248.       WriteLn('ARISING OUT OF YOUR USE OR INABILITY TO USE THE PROGRAM, OR FOR ANY');
  1249.       WriteLn('CLAIM BY ANY OTHER PARTY.');
  1250.       WriteLn;
  1251.       WriteLn('Usage:  UnZip FILE[.zip]');
  1252.       Halt;
  1253.     end;
  1254.  
  1255.   zipfn := ParamStr(1);
  1256.   if Pos('.', zipfn) = 0 then
  1257.     zipfn := zipfn+'.ZIP';
  1258.  
  1259.   extract_zipfile;
  1260. end.
  1261.  
  1262.  
  1263.